//
//  rotate_rect.cpp
//  OpenLearn
//
//  Created by korion chen on 2020/3/20.
//  Copyright © 2020 korion. All rights reserved.
//

#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "header/rotate_rect.hpp"
#include "header/model.h"

const float RotateRect::vertices[] = {
    0.5f, 0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, //右上
    0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   1.0f, 0.0f, //右下
    -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,   0.0f, 0.0f, //左下
    -0.5f, 0.5f, 0.0f,  1.0f, 1.0f, 0.0f,   0.0f, 1.0f  //左上
};

const uint32_t RotateRect::index[] = {
    0, 1, 3,
    1, 2, 3
};

int RotateRect::createShader(int type, const char *code){
    int shader = glCreateShader(type);
    glShaderSource(shader, 1, &code, NULL);
    glCompileShader(shader);
    int success;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        char info[512];
        glGetShaderInfoLog(shader, 512, NULL, info);
        std::cout << "ERROR::SHADER::COMPILATION_FAILED\n"<< info << std::endl;
        return 0;
    }
    return shader;
}


int RotateRect::createProgram(){
    int vertexShader = createShader(GL_VERTEX_SHADER, vertex_shader_code);
    int fragmentShader = createShader(GL_FRAGMENT_SHADER, fragment_shader_code);
    if (!vertexShader || !fragmentShader) {
        return 0;
    }
    uint32_t program = glCreateProgram();
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    glLinkProgram(program);
    int success;
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    if (!success) {
        char info[512];
        glGetProgramInfoLog(program, 512, NULL, info);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n"<< info << std::endl;
        return 0;
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    return program;
}

int RotateRect::loadTexture(const char* path, bool reverseY){
    ImageData *data = new ImageData(path);
    bool result = data->loadData(reverseY);
    if (!result) {
        delete(data);
        std::cout << "LOAD IMAGE FAIL" << std::endl;
        return 0;
    }
    uint32_t texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
    
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
 
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
    if (data->nrChannels == 3){
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, data->width, data->height, 0, GL_RGB, GL_UNSIGNED_BYTE, data->data);
    } else {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, data->width, data->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data->data);
    }
    
    glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, NULL);
    
    delete(data);
    return texture;
}

glm::mat4 RotateRect::getTransMatrix(float tran){
    glm::mat4 matrix = glm::mat4(1.0f);
    matrix = glm::translate(matrix, glm::vec3(0.5f, -0.5f, 0.0f));
    matrix = glm::rotate(matrix, glm::radians(180.0f * tran * 2), glm::vec3(0.0f, 0.0f, 1.0f));
//    matrix = glm::scale(matrix, glm::vec3(0.5f, 0.5f, 0.5f));
    return matrix;
}

bool RotateRect::init(){
    mProgram = createProgram();
    if (!mProgram) {
        return false;
    }
    
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);
    
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    int pos =  glGetAttribLocation(mProgram, "pos");
    glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(pos);
    
    int color =  glGetAttribLocation(mProgram, "color");
    glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(color);
    
    int texPos =  glGetAttribLocation(mProgram, "texPos");
    glVertexAttribPointer(texPos, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    
    glEnableVertexAttribArray(texPos);
    
    
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);
    
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    //不能在glBindVertexArray之前调用
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    
    texture0 = loadTexture("/Users/korionchen/WorkSpace/person/OpenGLlearn/OpenLearn/OpenLearn/res/container.jpg", false);
       
    texture1 = loadTexture("/Users/korionchen/WorkSpace/person/OpenGLlearn/OpenLearn/OpenLearn/res/awesomeface.png", true);
    
    glUseProgram(mProgram);
    //glUniform1i必须在glUseProgram之后
    glUniform1i((glGetUniformLocation(mProgram, "texture0")), 0);
    glUniform1i((glGetUniformLocation(mProgram, "texture1")), 1);
    
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture1);
    
    hasInit = true;
    return hasInit;
}

void RotateRect::drawSelf(GLFWwindow *window, float tran){
    if (!hasInit){
        return;
    }
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glBindVertexArray(VAO);
    glUniform1f(glGetUniformLocation(mProgram, "tran"), tran);
    glm::mat4 matrix = getTransMatrix(tran);
    glUniformMatrix4fv(glGetUniformLocation(mProgram, "matrix"), 1, GL_FALSE, glm::value_ptr(matrix));
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glfwSwapBuffers(window);
}

void RotateRect::destroy(){
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
}
